import {CPU} from './cpu.js'
import {PPU} from './ppu.js'
import {APU} from './apu.js'
import {Rom} from './rom.js'
import {Controller} from './controller.js'


export class FC{
    constructor(){
        this.cpu = CPU.getInstance(this)
        this.ppu = PPU.getInstance(this)
        this.apu = APU.getInstance(this)
        this.rom = Rom.getInstance()
        this.controller1 = new Controller(1)
        this.debugInfo = new Object()
    }

    // 绑定rom，绑定显示buffer
    reset(nesData,framebuffer_u32,debugMode,audioProcessBuffer){
        //从rom获取mapper编号
        //将rom信息按照mapper规则映射到内存
        this.rom.loadRom(nesData)
        // CPU复位，开始执行指令
        // 此处还需要把ppu的内存也提前映射好
        this.cpu.reset()
        // PPU复位，绑定显示buffer
        this.ppu.reset(framebuffer_u32)
        this.apu.reset(audioProcessBuffer)
        this.debugMode = debugMode
    }

    // 非debug模式自动刷新帧
    onFrame(){
        if(!this.debugMode){
            // this.frame()
            this.frameByStep()
        }
    }

    // 单帧前进
    runByFrame(){
        if(this.debugMode){
            this.frameByStep()
            // this.frame()
        }
    }

    // 按帧执行
    frame(){
        // 每帧的CPU周期为 1789772.5（主时钟频率）/ 60 = 29829
        var frameStartTime = new Date()
        var cpuCyclePerFrame = 29829
        var cpuCycleCount = 0
        let currentInstructionCycle = 0
        for(;;){
            // 先运行CPU一段时间，即运行29829次CPU
            currentInstructionCycle += this.cpu.execute()
            if(this.cpu.emulateEnd == 1){
                break
            }
            cpuCycleCount++
            // ppu的速度应该是cpu的3倍，真正硬件是一个周期写一个像素点的所有数据
            // 还需要完善
            if(currentInstructionCycle > cpuCyclePerFrame){
                var frameEndTime = new Date()
                var frameTime = frameEndTime - frameStartTime;
                // console.log('本帧CPU执行指令耗时：'+ frameTime +'ms')
                break
            }
        }
        // PPU频率时CPU的3倍
        var ppuCyclePerFrame = 89487
        // 再运行PPU，应输出一帧的全部像素，然后执行V-BLANK
        this.ppu.executeByScanline(this.ppu.vBuffer,this.cpu)
        // this.ppu.execute(this.ppu.vBuffer,this.cpu)
        // this.cpu.INTERRUPT_NMI = 1
        // console.log('end emulate')
        this.outPutDebugInfo()
    }

    // 逐条指令执行一帧
    frameByStep(){
        // 每帧的CPU周期为 1789772.5（主时钟频率）/ 60 = 29829
        var frameStartTime = new Date()
        var cpuCyclePerFrame = 29829
        var cpuPassedCycleCount = 0
        for(;;){
            cpuPassedCycleCount += this.step()
            // ppu的速度应该是cpu的3倍，真正硬件是一个周期写一个像素点的所有数据
            // 还需要完善
            if(cpuPassedCycleCount > cpuCyclePerFrame){
                var frameEndTime = new Date()
                var frameTime = frameEndTime - frameStartTime;
                // console.log('本帧CPU执行指令耗时：'+ frameTime +'ms')
                break
            }
        }
        this.apu.sample()
        // this.ppu.execute(this.ppu.vBuffer,this.cpu)
        // this.cpu.INTERRUPT_NMI = 1
        // console.log('end emulate')
        this.outPutDebugInfo()
    }

    // 按逐条指令执行(jsnes应该就是使用的此种同步方式)
    // 先执行CPU指令，再检查是否有NMI中断，可能会存在CPU和PPU执行的时钟数不一样的情况，但不影响实际应用
    step(){
        var cpuCycle = this.cpu.execute()
        var ppuCycle = 3 * cpuCycle
        this.ppu.executeByCycle(ppuCycle,this.cpu,this.ppu.vBuffer)
        this.apu.executeByCPUCycle(cpuCycle)
        return cpuCycle
    }

    outPutDebugInfo(){
        this.debugInfo.pc = this.cpu.PC
        this.debugInfo.sp = this.cpu.SP
        this.debugInfo.ps = this.cpu.getProcessorStatus()
        this.debugInfo.memory = this.cpu.memory.mem
    }

    toFormattedHex(num,length){
        var len = num.toString(16).length
        var outStr = num.toString(16).toUpperCase()
        while(len < length) {
            outStr = '0' + outStr
            len++
        }
        return outStr
    }
    sleep(time) {
        var startTime = new Date().getTime() + parseInt(time, 10);
        while(new Date().getTime() < startTime) {}
    }

    bindKey(key,action){
        if(key == 'a'){
            this.controller1.setButtonStatus('left',action)
        }
        if(key == 'd'){
            this.controller1.setButtonStatus('right',action)
        }
        if(key == 'w'){
            this.controller1.setButtonStatus('up',action)
        }
        if(key == 's'){
            console.log('preess S')
            this.controller1.setButtonStatus('down',action)
        }
        if(key == 'c'){
            this.controller1.setButtonStatus('select',action)
        }
        if(key == 'n'){
            this.controller1.setButtonStatus('start',action)
        }
        if(key == 'j'){
            this.controller1.setButtonStatus('b',action)
        }
        if(key == 'k'){
            this.controller1.setButtonStatus('a',action)
        }
    }

    setIRQ(){
        this.cpu.INTERRUPT_IRQ = 1
    }
}

// var fc = function(){
//     this.myLoader = new loader()
// }
// cpu.execute()
// fc.myLoader.loadHeader()